﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.RestrictedUsage.CSharp.Compiler;
using Microsoft.RestrictedUsage.CSharp.Core;
using Microsoft.RestrictedUsage.CSharp.Compiler.IDE;
using Microsoft.RestrictedUsage.CSharp.Syntax;
using Microsoft.RestrictedUsage.CSharp.Semantics;
using Microsoft.VisualStudio.CSharp.Services.Language.Interop;
using Microsoft.RestrictedUsage.CSharp.Extensions;
using devtm.Documentation.HierarchyModel;
using EnvDTE;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;


namespace CSharpParser.CSharpModel
{
    public class CSharpTextSelection
    {

        private readonly Compilation compilation;
        private readonly Position cursorPosition;
        private readonly Position endPosition;
        private static IDECompilerHost languageModel;
        private readonly FileName sourceFileName;


        // Methods
        public CSharpTextSelection(Compilation compilation, FileName sourceFileName)
            : this(compilation, sourceFileName, Position.Unknown, Position.Unknown)
        {
        }

        public CSharpTextSelection(Compilation compilation, FileName sourceFileName, Position cursorPosition)
            : this(compilation, sourceFileName, cursorPosition, cursorPosition)
        {
        }

        public CSharpTextSelection(Compilation compilation, FileName sourceFileName, Position cursorPosition, Position endPosition)
        {
            if (compilation == null)
            {
                throw new ArgumentNullException("compilation");
            }
            if (!compilation.SourceFiles.ContainsKey(sourceFileName))
            {
                throw new ArgumentException("Compilation doesn't contain source file");
            }
            this.compilation = compilation;
            this.sourceFileName = sourceFileName;
            this.cursorPosition = cursorPosition;
            this.endPosition = endPosition;
        }


        public CSharpMember FindContainingMember()
        {
            ParseTreeNode leafNode = this.FindLeafNode();
            if (leafNode == null)
            {
                return null;
            }
            MethodBaseNode containingNode = leafNode.GetContainingNode<MethodBaseNode>();
            if (containingNode == null)
            {
                return null;
            }
            return this.Compilation.GetMemberFromMemberDeclaration(containingNode);
        }


        public CSharpType FindContainingType()
        {
            ParseTreeNode leafNode = this.FindLeafNode();
            if (leafNode == null)
            {
                return null;
            }
            TypeBaseNode containingNode = leafNode.GetContainingNode<TypeBaseNode>();
            if (containingNode == null)
            {
                return null;
            }
            return this.Compilation.GetTypeFromTypeDeclaration(containingNode);
        }

        public Microsoft.RestrictedUsage.CSharp.Semantics.Expression FindLeafExpression(ParseTreeNode leafNode)
        {
            return this.FindLeafExpression(this.ParseTree, leafNode);
        }

        private Microsoft.RestrictedUsage.CSharp.Semantics.Expression FindLeafExpression(SourceFileParseTree sourceFileTree, ParseTreeNode leafNode)
        {
            ExpressionTree expressionTree = this.Compilation.FindExpressionTree(this.SourceFile, leafNode);
            if ((expressionTree != null) && (expressionTree.Root != null))
            {
                return expressionTree.FindLeafExpression(leafNode);
            }
            return null;
        }

        public ParseTreeNode FindLeafNode()
        {
            return this.ParseTree.FindLeafNode(this.cursorPosition);
        }

        public CSharpMember GetDeclaredMember(ParseTreeNode leafNode)
        {
            if (((leafNode.Parent != null) && (leafNode.Parent is MethodDeclarationNode)) && (leafNode == ((MethodDeclarationNode)leafNode.Parent).MemberName))
            {
                return this.Compilation.GetMemberFromMemberDeclaration(((MethodBaseNode)((MethodDeclarationNode)leafNode.Parent)));
            }
            if (((leafNode.Parent != null) && (leafNode.Parent is PropertyDeclarationNode)) && (leafNode == ((PropertyDeclarationNode)leafNode.Parent).MemberName))
            {
                return this.Compilation.GetMemberFromMemberDeclaration(((PropertyDeclarationNode)leafNode.Parent));
            }
            if (((leafNode.Parent != null) && (leafNode.Parent is VariableDeclaratorNode)) && (leafNode == ((VariableDeclaratorNode)leafNode.Parent).Identifier))
            {
                switch (ParseTreeMatch.GetVarDeclKind(leafNode.Parent))
                {
                    case ParseTreeMatch.VarDeclKind.vdkField:
                    case ParseTreeMatch.VarDeclKind.vdkEvent:
                    case ParseTreeMatch.VarDeclKind.vdkConst:
                    case ParseTreeMatch.VarDeclKind.vdkFixedField:
                        return this.Compilation.GetMemberFromMemberDeclaration(((VariableDeclaratorNode)leafNode.Parent));
                }
            }
            return null;
        }

        public CSharpNamespace GetDeclaredNamespace(ParseTreeNode leafNode)
        {
            if (leafNode.Parent != null)
            {
                NamespaceDeclarationNode parent = leafNode.Parent as NamespaceDeclarationNode;
            }
            return null;
        }

        public CSharpType GetDeclaredType(ParseTreeNode leafNode)
        {
            if (((leafNode.Parent != null) && (leafNode.Parent is TypeDeclarationNode)) && (leafNode == ((TypeDeclarationNode)leafNode.Parent).Identifier))
            {
                return this.Compilation.GetTypeFromTypeDeclaration(((TypeDeclarationNode)leafNode.Parent));
            }
            if (((leafNode.Parent != null) && (leafNode.Parent is DelegateDeclarationNode)) && (leafNode == ((DelegateDeclarationNode)leafNode.Parent).Identifier))
            {
                return this.Compilation.GetTypeFromTypeDeclaration(((DelegateDeclarationNode)leafNode.Parent));
            }
            return null;
        }

        public static CSharpTextSelection GetFromCaret()
        {
            if (GlobalServices.DTE.ActiveDocument == null)
            {
                return null;
            }
            if (GlobalServices.DTE.ActiveDocument.Language != "CSharp")
            {
                return null;
            }
            try
            {
                Project containingProject = GlobalServices.DTE.ActiveDocument.ProjectItem.ContainingProject;
                if (HierarchyUtilities.IsMiscellaneousProject(containingProject))
                {
                    return null;
                }
                IDECompiler compiler = LanguageModel.CreateCompiler(containingProject);
                TextSelection selection = GlobalServices.DTE.ActiveDocument.Selection as TextSelection;
                if (selection == null)
                {
                    return null;
                }
                return new CSharpTextSelection(compiler.GetCompilation(), new FileName(GlobalServices.DTE.ActiveDocument.FullName), new Position(selection.ActivePoint.Line - 1, selection.ActivePoint.LineCharOffset - 1), new Position(selection.AnchorPoint.Line - 1, selection.AnchorPoint.LineCharOffset - 1));
            }
            catch
            {
                return null;
            }
        }

        public static CSharpTextSelection GetFromPosition(Position position)
        {
            if (GlobalServices.DTE.ActiveDocument == null)
            {
                return null;
            }
            if (GlobalServices.DTE.ActiveDocument.Language != "CSharp")
            {
                return null;
            }
            try
            {
                Project containingProject = GlobalServices.DTE.ActiveDocument.ProjectItem.ContainingProject;
                if (HierarchyUtilities.IsMiscellaneousProject(containingProject))
                {
                    return null;
                }
                return new CSharpTextSelection(LanguageModel.CreateCompiler(containingProject).GetCompilation(), new FileName(GlobalServices.DTE.ActiveDocument.FullName), position, position);
            }
            catch
            {
                return null;
            }
        }

        public static void NavigateToSourceLocation(DisconnectedSourceLocation selectedSourceLocation)
        {
            NavigateToSourceLocation(selectedSourceLocation.FileName.Value, selectedSourceLocation.Position.Line, selectedSourceLocation.Position.Character);
        }

        public static void NavigateToSourceLocation(string fileName, int line, int column)
        {
            IVsUIHierarchy hierarchy;
            uint num;
            IVsWindowFrame frame;
            Guid empty = Guid.Empty;
            VsShellUtilities.OpenDocument(ServiceProvider.GlobalProvider, fileName, empty, out hierarchy, out num, out frame);
            if (frame != null)
            {
                Window windowObject = VsShellUtilities.GetWindowObject(frame);
                if (windowObject != null)
                {
                    TextSelection selection = windowObject.Selection as TextSelection;
                    if (selection != null)
                    {
                        selection.MoveToLineAndOffset(line + 1, column + 1, false);
                        windowObject.Activate();
                    }
                }
            }
            frame = null;
            hierarchy = null;
        }

        // Properties
        public Compilation Compilation
        {
            get
            {
                return this.compilation;
            }
        }

        public Position CursorPosition
        {
            get
            {
                return this.cursorPosition;
            }
        }

        public Position EndPosition
        {
            get
            {
                return this.endPosition;
            }
        }

        public static IDECompilerHost LanguageModel
        {
            get
            {
                if (languageModel == null)
                {
                    try
                    {
                        languageModel = new IDECompilerHost();
                    }
                    catch
                    {
                    }
                }
                return languageModel;
            }
        }

        public SourceFileParseTree ParseTree
        {
            get
            {
                return (SourceFileParseTree)this.SourceFile.GetParseTree();
            }
        }

        public SourceFile SourceFile
        {
            get
            {
                return this.Compilation.SourceFiles[this.sourceFileName];
            }
        }

        public FileName SourceFileName
        {
            get
            {
                return this.sourceFileName;
            }
        }


    }
}
